package com.syl.starter.itools.common.utils;

import com.syl.starter.itools.base.constants.BaseConstant;
import com.syl.starter.itools.common.enums.RoundModeEnum;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;

/**
 *  小数操作工具类
 * @author syl
 * @create 2018-10-17 17:06
 **/
public class DecimalUtils {
    private static final String VALID_CHARS = "#.0";
    private static final String DEFAULT_VALUE = "#0.00";
    public static final String ZERO = "0";

    /**
     * 简单的加法运算
     * @param decimals
     * @return
     */
    public static BigDecimal add(BigDecimal... decimals){
        return add(null, decimals);
    }

    /**
     * 加法运算 并保留x位小数
     * @param scale 结果的scale
     * @param decimals
     * @return
     */
    public static BigDecimal add(Integer scale, BigDecimal... decimals){
        BigDecimal result = new BigDecimal(0d);
        for (BigDecimal decimal : decimals) {
            result = result.add(decimal);
        }
        return scale(result, scale);
    }

    /**
     * 简单的对第一个小数做减法运算
     * @param decimals
     * @return
     */
    public static BigDecimal minus(BigDecimal... decimals){
        return minus(null, decimals);
    }

    /**
     * 对第一个小数做减法运算  并保留x位小数
     * @param decimals
     * @return
     */
    public static BigDecimal minus(Integer scale, BigDecimal... decimals){
        if(decimals == null || decimals.length < 1)
            return null;
        BigDecimal result = decimals[0];
        for (int i = 1; i < decimals.length; i++) {
            result = result.subtract(decimals[i]);
        }
        return scale(result, scale);
    }

    /**
     * 按2位小数形式格式化 默认按常规方式四舍五入
     * @param number
     * @return
     */
    public static String format(float number){
        return format(number, DEFAULT_VALUE);
    }

    /**
     * 默认使用常规四舍五入
     * @param number
     * @param pattern 格式化方式 例如 "#0.0"
     * @return
     */
    public static String format(float number, String pattern){
        return format(number, pattern, RoundModeEnum.ROUND);
    }

    /**
     * 格式化
     * @param number
     * @param pattern 格式化方式 例如 "#0.0"
     * @param modeEnum 四舍五入模式
     * @return
     */
    public static String format(float number, String pattern, RoundModeEnum modeEnum){
        return format(number, pattern, modeEnum, true);
    }

    /**
     * 按2位小数形式格式化 默认按常规方式四舍五入
     * @param number
     * @return
     */
    public static String format(double number){
        return format(number, DEFAULT_VALUE);
    }

    /**
     * 默认使用常规四舍五入
     * @param number
     * @param pattern 格式化方式 例如 "#0.0"
     * @return
     */
    public static String format(double number, String pattern){
        return format(number, pattern, RoundModeEnum.ROUND);
    }

    /**
     * 格式化
     * @param number
     * @param pattern 格式化方式 例如 "#0.0"
     * @param modeEnum 四舍五入模式
     * @return
     */
    public static String format(double number, String pattern, RoundModeEnum modeEnum){
        return format(number, pattern, modeEnum, true);
    }

    /**
     * 按2位小数形式格式化 默认按常规方式四舍五入
     * @param number
     * @return
     */
    public static String format(BigDecimal number){
        return format(number, DEFAULT_VALUE);
    }

    /**
     * 格式化小数 默认按常规方式四舍五入
     * @param number
     * @param pattern 格式化方式 例如 "#0.0"
     * @return
     */
    public static String format(BigDecimal number, String pattern){
        return format(number, pattern, RoundModeEnum.ROUND);
    }

    /**
     * 格式化
     * @param number
     * @param pattern 格式化方式 例如 "#0.0"
     * @param modeEnum 四舍五入模式
     * @return
     */
    public static String format(BigDecimal number, String pattern, RoundModeEnum modeEnum){
        if(number == null)
            return ZERO;
        String step = StringUtils.substringLastStep(pattern, BaseConstant.DOT);
        BigDecimal newNumber = scale(number, step.length() + 1, modeEnum);
        return format(newNumber, pattern, modeEnum, false);
    }

    /**
     * 以稍稍牺牲一点性能来解决 DecimalFormat 不能像我们平常熟悉的方式四舍五入
     * @param number 小数
     * @param pattern 格式化方法
     * @param modeEnum 舍入模式
     * @param special 特殊
     * @return
     */
    public static String format(Object number, String pattern, RoundModeEnum modeEnum, boolean special){
        if(number == null)
            return ZERO;
        DecimalFormat decimalFormat = new DecimalFormat(pattern);
        if(RoundModeEnum.UP == modeEnum)
            decimalFormat.setRoundingMode(RoundingMode.UP);
        if(RoundModeEnum.DOWN == modeEnum)
            decimalFormat.setRoundingMode(RoundingMode.DOWN);
        if(RoundModeEnum.ROUND == modeEnum && special){
            if(StringUtils.containsOnly(pattern, VALID_CHARS)) {// 当仅有#.0符号时才特殊处理
                String str = number.toString();
                str = str + 1;
                return decimalFormat.format(Double.valueOf(str));
            }
        }
        return decimalFormat.format(number);
    }

    /**
     * 保留小数位数 默认按正常的四舍五入方式
     * @param number
     * @param scale 保留位数
     * @return
     */
    public static BigDecimal scale(BigDecimal number, Integer scale){
        return scale(number, scale, RoundModeEnum.ROUND);
    }

    /**
     * 保留小数位数
     * @param number 目标数据
     * @param scale 保留位数
     * @param modeEnum 舍入模式
     * @return
     */
    public static BigDecimal scale(BigDecimal number, Integer scale, RoundModeEnum modeEnum){
        if(number == null || scale == null)
            return number;
        if(modeEnum == RoundModeEnum.ROUND) {
            return getExcessNumber(number, scale).setScale(scale, RoundingMode.HALF_UP);
        }
        if(RoundModeEnum.UP == modeEnum)
            return number.setScale(scale, RoundingMode.UP);
        if(RoundModeEnum.DOWN == modeEnum)
            return number.setScale(scale, RoundingMode.DOWN);
        return number.setScale(scale);
    }

    /**
     * 小数的特殊处理 要完成常规的四舍五入必须调用
     * @param scale
     * @return
     */
    private static BigDecimal getExcessNumber(BigDecimal result, Integer scale){
        BigDecimal pow = new BigDecimal(10).pow(scale + 1);
        return result.add(new BigDecimal(1).divide(pow));
    }

//    public static void main(String[] args) {
//        // BigDecimal 自带的几种进位策略都不能满足我们平常所见的四舍五入需求
//        // 这里就3种最常见的方法做示范
//        // 深入分析见 RoundingMode源码中的文档
//        BigDecimal price1 = new BigDecimal(0.35);
//        System.out.println("特例示例1: " + price1.setScale(1, RoundingMode.HALF_UP));
//
//        BigDecimal price2 = new BigDecimal(0.351);
//        System.out.println("特例示例2: " + price2.setScale(2, RoundingMode.UP));
//
//        BigDecimal price3 = new BigDecimal(0.355);
//        System.out.println("特例示例3: " + price3.setScale(2, RoundingMode.HALF_EVEN));
//
//        // 使用DecimalUtils后
//        System.out.println("示例1: " + DecimalUtils.scale(price1, 1));
//        System.out.println("示例2: " + DecimalUtils.scale(price2, 2));
//        System.out.println("示例3: " + DecimalUtils.scale(price3, 2));
//        System.out.println("结论： 使用DecimalUtils可以满足我们平常所见的四舍五入需求");
//    }

}
